package com.ruoyi.framework.shiro.util;

import com.ruoyi.framework.shiro.util.secret.MD5Util;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * 计算签名工具类；
 * 签名计算方法为：
 *
 * sign = MD5(自然排序(排除签名的所有参数) + [requestPath] + [requestMethod[POST|GET]] + secretKey);
 * 注意 requestMethod 已经经过大写转换处理；
 */
public class SignBuilder {
    public final static String TIMESTAMP_KEY = "timestamp";
    public final static String CLIENT_ID_KEY = "clientId";
    public final static String SIGN_KEY = "sign";
    public final static String SALT_KEY = "salt";

    private final String clientId;
    private final String clientSecret;

    private Long timestamp;

    private String salt;

    private String requestPath;
    private String requestMethod;

    private String sign;

    private final Map<String, Object> allParam = new TreeMap<String, Object>();


    private SignBuilder(String clientId, String clientSecret) {
        this.clientId = clientId;
        this.clientSecret = clientSecret;
    }


    public static SignBuilder create(String clientId, String clientSecret) {
        return new SignBuilder(clientId, clientSecret);
    }

    /**
     * 构建实例的时候 自动生成时间戳，通过 SignBuilder.getTimestamp() 获取时间戳
     * @param clientId OAuth2.0中客户端ID 也称为 appId
     * @param clientSecret OAuth2.0中客户端秘钥 也称为 appSecret
     */
    public static SignBuilder createWithTimestampAndSalt(String clientId, String clientSecret) {
        SignBuilder result = new SignBuilder(clientId, clientSecret);
        return result.timestamp(System.currentTimeMillis()).salt();
    }


    public SignBuilder param(String key, Object value) {
        if (sign != null) {
            throw new RuntimeException("sign has been generated, can't change the param");
        }

        if (SIGN_KEY.equals(key)) {
            return this;
        }

        if (TIMESTAMP_KEY.equals(key)) {
            this.timestamp = Long.valueOf(String.valueOf(value));
        }

        if (SALT_KEY.equals(key)) {
            this.salt = String.valueOf(value);
        }

        allParam.put(key, value);
        return this;
    }

    public SignBuilder params(Map<String, Object> params) {
        if (params == null) {
            return this;
        }

        for (Map.Entry<String, Object> entry : params.entrySet()) {
            param(entry.getKey(), entry.getValue());
        }
        return this;
    }

    /**
     * 设置时间戳
     * @param timestamp 时间戳
     */
    public SignBuilder timestamp(Long timestamp) {
        param(TIMESTAMP_KEY, timestamp);
        return this;
    }

    public SignBuilder requestPath(String requestPath) {
        if (sign != null) {
            throw new RuntimeException("sign has been generated, can't change the param");
        }
        this.requestPath = requestPath;
        return this;
    }

    public SignBuilder requestMethod(String requestMethod) {
        if (sign != null) {
            throw new RuntimeException("sign has been generated, can't change the param");
        }
        if (requestMethod != null) {
            this.requestMethod = requestMethod.toUpperCase();
        }
        return this;
    }

    public SignBuilder salt() {
        return salt(Math.random());
    }

    public SignBuilder salt(Object salt) {
        param(SALT_KEY, salt);
        return this;
    }

    /**
     * 获取 http 请求参数
     */
    public Map<String, Object> getQueryParamMap() {
        Map<String, Object> result = new HashMap<String, Object>(allParam);
        result.put(SIGN_KEY, sign());
        return result;
    }

    /**
     * debug使用，获取签名前的原文；<br>
     * 秘钥已脱敏，使用 <code>{clientSecret}</code> 代替
     */
    public String getRawDataBeforeSign() {
        return rawDataBeforeSign() + "{clientSecret}";
    }


    private String rawDataBeforeSign() {
        allParam.put(CLIENT_ID_KEY, clientId);

        if (timestamp != null && !allParam.containsKey(TIMESTAMP_KEY)) {
            allParam.put(TIMESTAMP_KEY, timestamp);
        }

        StringBuilder sb = new StringBuilder();
        for (Object value : allParam.values()) {
            if (value != null) {
                sb.append(String.valueOf(value).trim());
            }
        }
        if (requestPath != null) {
            sb.append(requestPath);
        }

        if (requestMethod != null) {
            sb.append(requestMethod.toUpperCase());
        }

        return sb.toString();
    }

    /**
     * 计算当前参数的签名，已经计算签名后，所有参数不可更改
     */
    public String sign() {
        if (sign != null) {
            return sign;
        }

        this.sign = MD5Util.getMD5String(rawDataBeforeSign() + clientSecret);
        if (this.sign == null) {
            return "";
        }
        return this.sign.toUpperCase();
    }

    public String getSalt() {
        return salt;
    }

    public String getClientId() {
        return clientId;
    }

    public Long getTimestamp() {
        return timestamp;
    }
}
